// AdaBoost.cpp: implementation of the CAdaBoost class.
//
//////////////////////////////////////////////////////////////////////

#include "AdaBoost.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CAdaBoost::CAdaBoost(int T)
{
	m_T = T;
	m_pbH = NULL;
	m_vWeakLearner.resize(m_T);
}

CAdaBoost::~CAdaBoost()
{
	if (m_pbH != NULL)
		delete m_pbH;
	m_pbH = NULL;
}

void CAdaBoost::LoadBinH(const char * strFileName)
{
	FILE *fp = fopen(strFileName, "rb");
	if (fp == NULL) return;

	if (m_pbH != NULL)
	{
		delete m_pbH;
		m_pbH = NULL;
	}

	fread(&m_nPos, sizeof(int), 1, fp);
	fread(&m_nNeg, sizeof(int), 1, fp);
	fread(&m_nFilters, sizeof(int), 1, fp);
	
	int n = (m_nPos + m_nNeg) * m_nFilters;
	m_pbH = new uchar[n];
	fread(m_pbH, sizeof(uchar), n, fp);
	fclose(fp);
}

/* Jiadi Yang
void CAdaBoost::LoadTextH(const char * strFiltersPath)
{
	printf("Loading filters ");

	FILE *fp;
    WIN32_FIND_DATA  fdata;
    LPWIN32_FIND_DATA lpffdata = &fdata;
	char fn[200], path[200], str[256];
	int i, cnt, n, v;

	if (m_pbH != NULL)
	{
		delete m_pbH;
		m_pbH = NULL;
	}

	// Get the number of filters
	strcpy(path, (char*)strFiltersPath);
	strcat(path, "*.txt");

	HANDLE h1 = FindFirstFile(path, lpffdata);
	if (h1==INVALID_HANDLE_VALUE){
		return;
	}
	m_nFilters = 0;
	do {
		m_nFilters ++;
	}while(FindNextFile(h1,lpffdata));

	// get number of positive and negative samples
	sprintf(fn, "%sf00000.txt",strFiltersPath);
	fp = fopen(fn,"rt");
	fgets(str, 255, fp);  // bypass filter description
	fgets(str, 255, fp);  // bypass filter description
	fgets(str, 255, fp);  // bypass filter description
	fscanf(fp, "%d%d", &m_nPos, &m_nNeg);
	n = m_nPos + m_nNeg;
	m_pbH = new BYTE[n * m_nFilters];
	// read h
	for (cnt = 0; cnt < n; cnt++)
	{
		fscanf(fp, "%d", &v);
		m_pbH[cnt] = v;
	}
	fclose(fp);

	// read other h's
	for (i = 1; i < m_nFilters; i ++)
	{
		if (i%200 == 1)
			printf(".");
		sprintf(fn, "%sf%05d.txt", strFiltersPath, i);
		fp = fopen(fn, "rt");
		fgets(str, 255, fp);  // bypass filter description
		fgets(str, 255, fp);  // bypass filter description
		fgets(str, 255, fp);  // bypass filter description
		fgets(path, 180, fp);  // bypass number of positive and negative samples
		for (cnt = 0; cnt < n; cnt++)
		{
			fscanf(fp, "%d", &v);
			m_pbH[i * n + cnt] = v;
		}
		fclose(fp);
	}
	printf(" Done!\n");
}
*/

void CAdaBoost::NormWeights()
{
	int nWeights = m_vWeights.size();
	double sum = 0;
	int i; 
	for ( i = 0; i < nWeights; i ++)
	{
		sum += m_vWeights[i];
	}

	for (i = 0; i < nWeights; i ++)
	{
		m_vWeights[i] /= sum;
	}
}

//
//  AdaBoost from Viola's method
//  return 0 for success, 1 otherwise
//
int CAdaBoost::Boost(double fPosW)
{
	// init weights
	// double fPosW = 0.98;
	double fNegW = 1 - fPosW;
	int i;
	for (i = 0; i < m_nPos; i ++)
		m_vWeights.push_back(fPosW/m_nPos);

	for (i = 0; i < m_nNeg; i ++)
		m_vWeights.push_back(fNegW/m_nNeg);

	m_SumAlpha = 0;
	for (int t = 0; t < m_T; t++)
	{
		printf("t=%3d  ", t);
		CWeakLearner &wl = m_vWeakLearner[t];
		wl = MinErrorWeakLearner();
		if (wl.er >= 0.5)
		{
			m_T = t;
			m_vWeakLearner.resize(m_T);
			printf("Change T to %d\n", m_T);
			break;
		}
		m_SumAlpha += wl.alpha;

		printf("Filter NO.%4d ", wl.fea_no);
		printf("error = %5.3lf\n", wl.er);
		
		if (wl.alpha >= 99999)
		{
			m_T = t+1;
			m_vWeakLearner.resize(m_T);
			printf("Change T to %d\n", m_T);
			break;
		}

		UpdateWeights(wl);
		NormWeights();
	}
	m_SumAlpha /= 2;

	return 0;
}

//
// the t-th feature in boot procedure
//
CWeakLearner CAdaBoost::MinErrorWeakLearner()
{
	if (m_pbH == NULL)
	{
		return CWeakLearner(-1, 0.5);
	}

	int i,j;
	int nSamples = m_vWeights.size();
	double minerr = 100;
	int minidx;
	for (i = 0; i < m_nFilters; i++)
	{
		double err = 0;
		for (j = 0; j < nSamples; j ++)
		{
			err += m_vWeights[j] * m_pbH[i*nSamples+j];
		}
		if (err < minerr)
		{
			minerr = err;
			minidx = i;
		}
	}

	if (minerr < 0) minerr = 0;
	return CWeakLearner(minidx, minerr);
}

void CAdaBoost::UpdateWeights(CWeakLearner& wl)
{
	int nSamples = m_vWeights.size();
	uchar *FeaH = m_pbH + wl.fea_no * nSamples;
	for (int i = 0; i < nSamples; i ++)
	{
		if (!FeaH[i]) // Correctly classified
		{
			m_vWeights[i] *= wl.beta;
		}
	}
}

void CAdaBoost::Verify()
{
	int n = m_nPos + m_nNeg;
	// count false negative 
	int cnt = 0;  
	int i, t;
	for (i = 0; i < m_nPos; i++)
	{
		double w = 0;
		for (t = 0; t < m_T; t++)
		{
			CWeakLearner &wl = m_vWeakLearner[t];
			w += (1-m_pbH[n*wl.fea_no+i])*wl.alpha;
		}
		if (w <= m_SumAlpha)
		{
//			printf("False Negative: %d\n", i);
			cnt ++;
		}
	}
	printf("Number of False Negative: %d\n", cnt);

	// count false positive
	cnt = 0;  
	for (i = 0; i < m_nNeg; i++)
	{
		double w = 0;
		for (int t = 0; t < m_T; t++)
		{
			CWeakLearner &wl = m_vWeakLearner[t];
			w += m_pbH[n*wl.fea_no+m_nPos+i]*wl.alpha;
		}
		if (w > m_SumAlpha)
		{
//			printf("False Positive: %d\n", i);
			cnt ++;
		}
	}
	printf("Number of False Positive: %d\n", cnt);
}

int CAdaBoost::Classify(const char *strFileName)
{
	double sum = 0;
	return sum > m_SumAlpha;
}

void CAdaBoost::WriteClassifier(const char *strFileName, const char * strFiltersPath)
{
	char fn[256], str[256];

	printf("Writing Classifier ... ");
	FILE *fp = fopen(strFileName, "wt");
	if (fp == NULL)
		return;
	fprintf(fp, "//	{{ Classifier\n");
	for (int t = 0; t < m_T; t++)
	{
		CWeakLearner &wl = m_vWeakLearner[t];
		sprintf(fn, "%sf%05d.txt", strFiltersPath, wl.fea_no);
		FILE *ff = fopen(fn, "rt");
		for (int i = 0; i < 3; i++)
		{
			fgets(str, 255, ff);
			fputs(str, fp);
		}
		fclose(ff);
		fprintf(fp, "		fRes += %lff;\n",wl.alpha);
		fprintf(fp, "	fSumAlpha += %lff;\n",wl.alpha);
		fprintf(fp, "	t++;\n");
	}
	fclose(fp);
	printf("Done!\n");
}


void CAdaBoost::GetClassifiers(vfloat &vfAlpha, vint &vnClassifiers)
{
	for (int i = 0; i < m_T; i++)
	{
		CWeakLearner &wl = m_vWeakLearner[i];
		vfAlpha.push_back(wl.alpha);
		vnClassifiers.push_back(wl.fea_no);
	}
}
